let id = 0
import Dep from "./dep";
// 每个属性有个一个dep 属性相当于被观察者   watcher就是观察者（属性变了会通知观察者来更新） 观察者模式
class Watcher {
    constructor(vm, expOrFn, option, cb) {  //  new Watcher(this, expOrFn, { user: true }, cb)
        this.user = !!option.user // 是否为用户watcher
        this.id = id++
        this.option = option // boolean 是否为渲染watcher
        if (typeof expOrFn === 'function') {  // 当为watchWatcher时且 值为字符串时 调用method上方法
            this.getter = expOrFn //  渲染方法  
        } else {
            this.getter = function () {
                let path = expOrFn.split('.')
                let obj = vm
                for (let i = 0; i < path.length; i++) {
                    obj = obj[path[i]]
                }
                return obj
            }
        }
        this.user = option.user // 是否为用户的watch方法
        this.dep = [] //  为了computed方法,组件卸载和一些清理数据的场景， 需要让watcher记住本页面的dep
        this.depIds = new Set()
        this.vm = vm
        this.cb = cb
        this.lazy = option.lazy  // 是否为渲染watcher 即 是否立即执行渲染方法
        this.flag = this.lazy //用来判断此computed是否已经被使用过 
        this.value = this.lazy ? undefined : this.get() // 非渲染watcher 不执行渲染方法
    }
    evaluate() {
        this.value = this.get()
        this.flag = false
    }
    get() {

        Dep.addStack(this) // 渲染之前将当前watcher赋值给Dep上的静态属性target （独一份）
        // 渲染watcher时getter为渲染方法， computed的watcher时getter为用户自定义get方法
        let value = this.getter.call(this.vm)
        Dep.popStack()  // 渲染完成之后清空
        return value
    }
    addDep(dep) { // 添加watcher 使用的dep 并去重
        let depId = dep.id
        if (!this.depIds.has(depId)) {
            this.dep.push(dep)
            this.depIds.add(depId)
            dep.addSub(this)
        }

    }
    depend() {
        let i = this.dep.length
        while (i--) {
            this.dep[i].depend()
        }
    }
    update() {
        if (this.lazy) {
            this.flag = true
        } else {
            queueWatcher(this)
        }
    }
    run() {
        let value = this.get();
        let oldValue = this.value
        this.value = value
        if (this.user) {
            this.cb.call(this.vm, value, oldValue)
        }
        this.get()
    }

}
//-----------------------watcher队列 异步执行--------------------

let asyncFunc;

if (Promise) {
    asyncFunc = () => {
        Promise.resolve().then(flushSchedulerQueue)
    }
} else if (MutationObserver) {
    let observer = new MutationObserver(flushSchedulerQueue)
    let textNode = document.createTextNode(1)
    observer.observe(textNode, {
        characterData: true
    })
    asyncFunc = () => {
        textNode.textContent = 2
    }
} else if (setImmediate) {
    asyncFunc = () => {
        setImmediate(flushSchedulerQueue)
    }
} else {
    asyncFunc = () => setTimeout(flushSchedulerQueue, 0)
}



function flushSchedulerQueue() {
    let arr = queue.slice(0)
    queue = []
    map = {}
    isPending = false
    arr.forEach(v => v.run())
}

let queue = []
let map = {}
let isPending = false

function queueWatcher(watcher) {

    let id = watcher.id
    if (!map[id]) {
        queue.push(watcher)
        map[id] = true
    }
    if (!isPending) {

        isPending = true
        asyncFunc()
        // setTimeout(flushSchedulerQueue, 0);
    }

}
//  -----同步变异步 优雅降级---------------

let timerFunc;

if (Promise) {
    timerFunc = () => {
        Promise.resolve().then(flushCallbacks)
    }
} else if (MutationObserver) {
    let observer = new MutationObserver(flushCallbacks)
    let textNode = document.createTextNode(1)
    observer.observe(textNode, {
        characterData: true
    })
    timerFunc = () => {
        textNode.textContent = 2
    }
} else if (setImmediate) {
    timerFunc = () => {
        setImmediate(flushCallbacks)
    }
} else {
    timerFunc = () => setTimeout(flushCallbacks, 0)
}


//-----------nextTick 队列化 异步执行----------
let callbacks = []
let waiting = false

function flushCallbacks() {
    let arr = callbacks.slice(0)
    callbacks = []
    waiting = false
    arr.forEach(v => v())

}

export function nextTick(cb) {
    callbacks.push(cb)
    if (!waiting) {
        waiting = true
        timerFunc()
        // setTimeout(() => {
        //     flushCallbacks()
        // }, 0);
    }

}
export default Watcher